package com.yt.bigdata;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.time.Instant;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

public class ProcessInstanceExecCacheWithRemovalManagerImpl {
    protected final Logger logger = LoggerFactory.getLogger(ProcessInstanceExecCacheWithRemovalManagerImpl.class);


    //检查：起始时间
    private final long initialDelayMinutes = 1;
    //检查：间隔时间
    private final long periodMinutes = 10;
    //超时删除时间
    private final String defaulIintervalDayValue = "1";

    private final ConcurrentHashMap<Object, Obj> cacheMap;
    private final ScheduledExecutorService scheduler;


    private long getInterlMillis() {
        String intervalDayKey = "remove.taskinfo.intervalDay";
        String intervalDayValue = System.getProperty(intervalDayKey, System.getenv(intervalDayKey));
        if (StringUtils.isBlank(intervalDayValue)) {
            //默认一天
            intervalDayValue = defaulIintervalDayValue;
        }
        long intervalMillis = TimeUnit.DAYS.toMillis(Integer.parseInt(intervalDayValue));
        return intervalMillis;
    }

    private void removeProcess() {
        cacheMap.entrySet().removeIf(entry -> {
            Optional<Obj> objOption = Optional.ofNullable(entry.getValue());
            if (objOption.isPresent()) {
                Obj obj = objOption.get();
                //超时删除
                return obj.getRemoveMillis() > 0 && Instant.now().toEpochMilli() - obj.getRemoveMillis() >= getInterlMillis();
            }
            return true;
        });
    }


    public ProcessInstanceExecCacheWithRemovalManagerImpl() {
        cacheMap = new ConcurrentHashMap<>();
        scheduler = Executors.newSingleThreadScheduledExecutor();
        scheduler.scheduleAtFixedRate(
                this::removeProcess,
                initialDelayMinutes,
                periodMinutes,
                TimeUnit.MINUTES
        );
        logger.info("ProcessInstanceExecCacheWithRemovalManagerImpl init.");
        Runtime.getRuntime().addShutdownHook(new Thread(() -> {
            logger.info("ProcessInstanceExecCacheWithRemovalManagerImpl shutdown.");
            scheduler.shutdown();
        }));
    }

    @Data
    @NoArgsConstructor
    @AllArgsConstructor
    private static class Obj<K, V> {
        private K key;
        private V value;
        private long removeMillis = 0;
    }


    public <K, V> void put(K key, V value) {
        cacheMap.put(key, new Obj(key, key, 0));
    }

    public <K, V> V get(K key) {
        Optional<Obj> objOption = Optional.ofNullable(cacheMap.get(key));
        if (objOption.isPresent()) {
            Obj obj = objOption.get();
            if (obj.getRemoveMillis() > 0) {
                obj.setRemoveMillis(Instant.now().toEpochMilli());
            }
            return (V) obj.getValue();
        }
        return null;
    }

    public <V> List<V> getALl() {
        return cacheMap.values()
                .stream()
                .filter(Objects::nonNull)
                .map(x -> (V) x.getValue())
                .collect(Collectors.toList());
    }


    public <K, V> V remove(K key) {
        Optional<Obj> objOption = Optional.ofNullable(cacheMap.get(key));
        if (objOption.isPresent()) {
            Obj obj = objOption.get();
            obj.setRemoveMillis(Instant.now().toEpochMilli());
            return (V) obj.getValue();
        }
        return null;
    }


    public static void main(String[] args) throws InterruptedException {
        ProcessInstanceExecCacheWithRemovalManagerImpl cacheWithRemoval = new ProcessInstanceExecCacheWithRemovalManagerImpl();
        cacheWithRemoval.put(1, 1111);
        cacheWithRemoval.put(2, 2222);

        cacheWithRemoval.remove(1);

    }
}